package SSF.OS.UDP;

/*
 * udpSessionMaster.java
 *
 * Created: Wed Nov 3 1999 hongbol
 */
import SSF.OS.*;
import SSF.OS.Socket.*;
import SSF.Net.Util.*;
import java.util.*;
import com.renesys.raceway.DML.*;

/** Main UDP ProtocolSession in the ProtocolGraph. Opens a UDP session
 *  when an application opens a UDP socket, and manages a list of
 *  open UDP sessions. Demultiplexes incoming UDP messages to appropriate
 *  sessions.
 */
public class udpSessionMaster extends ProtocolSession {

  /********************* Attribute Variables *********************/

  /** List of opened UDP sessions: if passively opened via socket bind(),
   *  a UDP session is identified only by local port and has
   *  remote IP = remote port = -1;
   *  if actively opened via socket connect(), a UDP session is identified
   *  by local port, remote IP and remote port; 
  */
  Vector allUDPsessions;

  /** SSF.OS.IP instance in the ProtocolGraph */
  ProtocolSession ipsess;

  /** SSF.OS.Socket instance in the ProtocolGraph */
  ProtocolSession socketsess;

  /** print out debug information */
  boolean debug = false;

  /** default maximum datagram size in virtual bytes */
  public int max_datagram_size = 10000;

  /************************* Constructors ************************/

  public udpSessionMaster() {}

  /************************ Class Methods ***********************/

  /** initialize the udpSessionMaster, get the instance of Socket session and
   *  IP session.
   */
  public void init()throws ProtocolException {
    ipsess = inGraph().SessionForName("ip");
    ipsess.open(this, null);
    socketsess = inGraph().SessionForName("socket");
    socketsess.open(this,null);
    // a client is likely to have one session; a server - many
    allUDPsessions = new Vector(1,5);
  }

  /** Supported DML attributes:
   * <PRE>
   *   ProtocolSession [name udp use SSF.OS.UDP.udpSessionMaster
   *     udpinit [
   *       max_datagram_size %I   # max UDP datagram size (payload bytes, int)
   *       debug %S               # print verbose UDP diagnostics, true/false
   *     ]
   *   ]
   * </PRE>
   */
  public void config(Configuration cfg)throws configException{
    super.config(cfg);
    try {
      Configuration ucfg;
      Object udpinit  = cfg.findSingle("udpinit");

      if (udpinit == null) return;
      ucfg = (Configuration)udpinit;
      if((ucfg.findSingle("debug") != null) && 
         (((String)ucfg.findSingle("debug")).compareTo("true") == 0))
        debug =  true;
      
      String str = (String)ucfg.findSingle("max_datagram_size");
      if( str != null)
        max_datagram_size = (new Integer(str)).intValue();
 
    } catch (configException e) {
        System.err.println("UDP: " + e); 
    }
  }

  /** open a UDP session - called by a socket */
  public udpSession openSession(udpSocket sock){
    udpSession udp_s = new udpSession(this, sock);
    allUDPsessions.addElement(udp_s);
    return udp_s;
  }

  /** close a UDP session - called by a socket */
  public void closeSession(udpSession udpsess){
    allUDPsessions.removeElement(udpsess);
  }

  /** Look for a session in the UDP session list by its identification of
   *  local port number, and remote IP address and port number.
   *  A "listening" server UDP session  will have remote (dest) IP address
   *  and port number both equal to -1.
   *  First search for a UDP session that matches all 3 identifiers; if not
   *  found, search for a server session that matches local Port; if not found
   *  return null.
   */
  public udpSession FindSession(int locPort, int destIpAdr, int destPort){
    udpSession udp_s;
    udpSession serv_s = null;
    for(Enumeration list = allUDPsessions.elements(); list.hasMoreElements();){
      udp_s = (udpSession)list.nextElement();
      if(udp_s.local_port == locPort) {
        if(udp_s.remote_ip == destIpAdr && udp_s.remote_port == destPort) {
          return udp_s;
        } else {
          if(udp_s.remote_ip == -1 && udp_s.remote_port == -1)
            serv_s = udp_s;
        }
      }
    }
    return serv_s;
  }

  /** demux a UDP message from IP to proper UDP session */
  public boolean push(ProtocolMessage message, ProtocolSession fromSession) 
    throws ProtocolException{

    if (fromSession instanceof IP) {
      UDP_Header udp_msg = (UDP_Header)message;
      if(debug) printMsg(udp_msg);

      int dPort = udp_msg.DEST_port;
      int sPort = udp_msg.SOURCE_port;
      int sIpAddr = ((IpHeader)udp_msg.previous()).SOURCE_IP;

      // is there an open UDP session expecting this message
      // note: 'source' in incoming message header is 'dest' in local socket!!
      udpSession udp_s = FindSession(dPort, sIpAddr, sPort);
      if(udp_s != null){
        return udp_s.push(message, fromSession);
      } 
    }
    return false;  // dropping UDP message
  }

  /** print out the content of a UDP message*/
  public void printMsg(UDP_Header hdr){
    System.out.println(inGraph().now()/(double)SSF.Net.Net.seconds(1.0)
                       + " UDP rcv [ IPhdr: s "
                       + IP_s.IPtoString(((IpHeader)hdr.previous()).SOURCE_IP)
                       + " d " + IP_s.IPtoString(((IpHeader)hdr.previous()).DEST_IP) 
                       + "]"
                       + hdr.toString());
  }
}
